use std::{collections::HashMap, fs::{remove_file, File}, io::BufReader, path::PathBuf, sync::Arc, time::Duration};

use wtask_app::{App, StreamStatus};
use wtask_base::{
    logs::DateTime,
    cmd::create_file,
    error::{WError, WResult},
    export::{Local, TimeZone, TokioRwLock, log_debug, log_error, log_info, select, serde_from_reader, serde_from_slice, tokio_sleep, tokio_spawn},
    machine::{CMDArgsConfigMachine, CMDMachineArgs, WSpawn, WTASK_CLI_CONFIG_FILE, WTASK_MACHINE_CONFIG_FILE, get_file_maching_running, get_machine_token, machine_port, write_config_to_file},
    tcp::{TCPClientType, TOKEN_ALIVE, WTcpStream, stream_read_raw, stream_read_token, tcp_client_session_start, tcp_client_start, tcp_listen, tcp_server_session_start}
};


fn load_machine_args_from_file(file: &str) -> WResult<CMDMachineArgs> {
    let file_config = PathBuf::from(file);
    if !file_config.exists() {
        return Err(WError::DataError(format!("file config not exist: {file_config:?}")));
    }
    let f = File::open(file_config)?;
    let r = BufReader::new(f);
    let data = serde_from_reader(r)?;
    Ok(data)
}



pub fn machine_args_to_proxy_and_local(args: CMDMachineArgs) -> WResult<(CMDArgsConfigMachine, CMDArgsConfigMachine)> {
    if let Some(file_config) = args.file_config {
        let args = load_machine_args_from_file(&file_config)?;
        return machine_args_to_proxy_and_local(args);
    }

    let g = if let Some(t) = args.server_token {
        t
    } else {
        get_machine_token()?
    };
    if g.len() < 32 || g.len() > 128 {
        return Err(WError::DataError(format!("server token length error: {g}")));
    }
    let config_proxy = CMDArgsConfigMachine {
        machine_ip: args.proxy_ip,
        machine_port: args.proxy_port,
        proxy: true,
        machine_token: args.proxy_machine_token.unwrap_or(g.clone()),
    };
    let config_local = CMDArgsConfigMachine {
        machine_ip: "0.0.0.0".to_string(),
        machine_port: machine_port(args.local_port, false),
        proxy: false,
        machine_token: args.local_machine_token.unwrap_or(g),
    };
    Ok((config_local, config_proxy))
}





async fn stream_func_all(server_token: &str, session_token: &str, stream_session: WTcpStream, stream_status: StreamStatus) -> WResult<()>{
    let req = stream_read_raw(stream_session.0.as_ref(), session_token).await?;
    match serde_from_slice::<App>(&req) {
        Ok(app) => app.server_handle(server_token, session_token, &stream_session, stream_status).await?,
        Err(e) => {
            log_debug!("app tyep parse error: {e}");
        }
    }
    Ok(())
}


async fn proxy_run_loop(
    server_token: String,
    stream_master: WTcpStream,
    config: CMDArgsConfigMachine,
    auth: DateTime,
) -> WResult<()>{
    let mut wspawn = WSpawn::default();
    let (_t, mut kill_rx) = wspawn.catch_pause_signal(async move {
        let _ = remove_file(get_file_maching_running());
    });
    let stream_status = Arc::new(TokioRwLock::new((kill_rx.clone(), HashMap::new(), HashMap::new())));
    loop {
        select! {
            Ok(session_token) = stream_read_token(stream_master.0.as_ref(), &server_token) => {
                if session_token.eq(TOKEN_ALIVE) {
                    continue;
                }
                // auth
                if Local::now() > auth {
                    log_error!("Auth error, winter_lonely@foxmail.com");
                    break;
                }
                log_debug!("Machine session token: {server_token}");
                let server_token = server_token.clone();
                let config = config.clone();
                let stream_status_clone = stream_status.clone();
                wspawn.spawn(async move {
                    if let Ok(stream_session) = tcp_client_session_start(
                        &config.machine_ip,
                        config.machine_port,
                        &server_token,
                        &session_token
                    ).await {
                        let _ = stream_func_all(
                            &server_token,
                            &session_token,
                            stream_session,
                            stream_status_clone,
                        ).await;
                    }
                });
            }
            _ = kill_rx.changed() => {
                log_info!("Shutting proxy ...");
                break;
            }
        }
    }
    Ok(())
}



async fn local_run_single(
    stream: WTcpStream,
    server_token: String,
    port_status: StreamStatus,
) -> WResult<()>{
    let session_token = tcp_server_session_start(&stream, &server_token).await?;
    let _ = stream_func_all(
        &server_token,
        &session_token,
        stream,
        port_status
    ).await;
    Ok(())
}



async fn local_run_loop(
    server_token: String,
    port: u32,
    auth: DateTime,
) -> WResult<()>{
    let listener = tcp_listen(port, "Machine")?;
    let mut wspawn = WSpawn::default();
    let (_t, mut kill_rx) = wspawn.catch_pause_signal(async move {});
    let stream_status = Arc::new(TokioRwLock::new((kill_rx.clone(), HashMap::new(), HashMap::new())));
    loop {
        select! {
            Ok((stream, _)) = listener.accept() => {
                // auth
                if Local::now() > auth {
                    log_error!("Auth error, winter_lonely@foxmail.com");
                    break;
                }
                // log_debug!("Server get a connect ...");
                let server_token_clone = server_token.clone();
                let stream_status_clone = stream_status.clone();
                wspawn.spawn(async move {
                    let stream = stream.into_split();
                    let _ = local_run_single(stream, server_token_clone, stream_status_clone).await;
                });
            }
            _ = kill_rx.changed() => {
                log_info!("Shutting machine ...");
                break;
            }
        }
    }
    Ok(())
}




pub async fn server(config_proxy: CMDArgsConfigMachine, config_local: CMDArgsConfigMachine) -> WResult<()> {    
    write_config_to_file(
        vec![
            config_local.clone(),
            config_proxy.clone(),
        ],
        WTASK_MACHINE_CONFIG_FILE
    )?;
    write_config_to_file(
        vec![
            config_local.clone(),
        ],
        WTASK_CLI_CONFIG_FILE
    )?;
    let auth = Local.with_ymd_and_hms(2026, 3, 1, 1, 1, 1).unwrap();
    // file running
    let server_token = config_local.machine_token.clone();
    log_info!("Machine Auth Token: {server_token}");
    let server_port = config_local.machine_port;
    tokio_spawn(async move {
        tokio_sleep(Duration::from_secs(5)).await;
        let p = get_file_maching_running();
        let _ = create_file(&p, &format!("{server_token}\n{server_port}"));
    });
    // proxy
    let auth_clone = auth;
    tokio_spawn(async move {
        let server_token = config_proxy.machine_token.clone();
        let stream_master = tcp_client_start(
            &config_proxy.machine_ip,
            config_proxy.machine_port,
            &server_token,
            &TCPClientType::Master
        ).await;
        match stream_master {
            Ok(stream_master) => {
                log_info!("Connected proxy: {}", config_proxy.machine_ip);
                let _ = proxy_run_loop(
                    server_token,
                    stream_master,
                    config_proxy,
                    auth_clone
                ).await;
            },
            Err(e) => {
                log_error!("Proxy server connect stop, error: {e}");
            }
        }
    });
    // local
    let server_token = config_local.machine_token;
    match local_run_loop(
        server_token,
        config_local.machine_port,
        auth
    ).await {
        Ok(_) => {},
        Err(e) => {
            log_error!("Local server stop, error: {e}");
        }
    }
    let _ = remove_file(get_file_maching_running());
    Ok(())
}

